﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TacticsMoveMonoBehaviour : MonoBehaviour
{
    public bool isMyTurn = false;

    public bool isMoving = false;
    public bool isFallingDown = false;
    public bool isJumpingUp = false;
    public bool isMovingEdge = false;

    public int move = 5;
    public float jumpHeight = 0.5f;
    public float moveSpeed = 0.5f;

    public TileMonoBehaviour actualTargetTile;

    float halfHeight = 0;

    Vector2 heading = new Vector2();
    Vector3 moveVelocity = new Vector3();
    public float jumpVelocity = 10f;
    //Vector2 jumpTargetPosition = new Vector2();

    TileMonoBehaviour currentTile;
    
    List<TileMonoBehaviour> selectableTiles = new List<TileMonoBehaviour>();
    Stack<TileMonoBehaviour> path = new Stack<TileMonoBehaviour>();

    GameObject[] tiles;

    //Vector2 tempTargetPosition = Vector2.zero;
    protected Collider2D currentCollider;

    public void GetCurrentTile()
    {
        currentTile = GetTargetTile(gameObject);
        if(currentTile != null)
        {
            currentTile.isCurrent = true;
            currentTile.isSelectable = false;
            currentTile.isOcupied = true;
        }
    }

    public TileMonoBehaviour GetTargetTile(GameObject target)
    {
        TileMonoBehaviour tile = null;

        Collider2D[] colliders = Physics2D.OverlapPointAll(target.transform.position);

        foreach(Collider2D collider in colliders)
        {
            tile = collider.GetComponent<TileMonoBehaviour>();
            if (tile)
            {
                break;
            }
        }
        
        return tile;
    }

    protected void Init()
    {
        tiles = GameObject.FindGameObjectsWithTag("Tile");

        if (currentCollider == null)
        {
            currentCollider = GetComponent<Collider2D>();
        }

//        TurnManager.AddUnit(this);
    }

    public void ComputeAdjacencyList(TileMonoBehaviour targetTile)
    {
        //tiles = GameObject.FindGameObjectsWithTag("Tile");

        foreach (GameObject tile in tiles)
        {
            TileMonoBehaviour t = tile.GetComponent<TileMonoBehaviour>();
            if (t)
            {
                t.FindNeighbors(targetTile);
            }
        }
    }

    public void FindSelectableTiles()
    {
        ComputeAdjacencyList(null);
        GetCurrentTile();

        Queue<TileMonoBehaviour> process = new Queue<TileMonoBehaviour>();

        process.Enqueue(currentTile);
        if (currentTile == null)
        {
            return;
        }

        //currentTile.parentTile = null;

        while(process.Count > 0)
        {
            TileMonoBehaviour t = process.Dequeue();

            selectableTiles.Add(t);
            t.isSelectable = true;

            if (t.distance < move)
            {
                foreach (TileMonoBehaviour tile in t.adjacencyList)
                {
                    if (!tile.isVisited && !tile.isOcupied)
                    {
                        tile.parentTile = t;
                        tile.isVisited = true;
                        tile.distance = 1 + t.distance;

                        process.Enqueue(tile);
                    }
                }
            }
        }
    }

    public void MoveToTile(TileMonoBehaviour tile)
    {
        path.Clear();

        tile.isTarget = true;
        isMoving = true;

        TileMonoBehaviour next = tile;

        while (next != null)
        {
            next.isSetPath = true;
            path.Push(next);
            next = next.parentTile;
        }
    }


    public void Move()
    {
        if (currentTile != null)
        {
            currentTile.isOcupied = false;
        }

        if (path.Count > 0)
        {
            TileMonoBehaviour t = path.Peek();
            Vector2 targetPosition = t.transform.position;
            
            // Calculate the unit's position on top of the target tile
            targetPosition.y = halfHeight + t.transform.position.y;

            if (Vector2.Distance(transform.position, targetPosition) >= 0.05f)
            {
                CalculateHeading(targetPosition);
                SetHorizontalVelocity();
                transform.position += moveVelocity * Time.deltaTime;
            }
            else
            {
                // Tile conter reached
                transform.position = targetPosition;
                path.Pop();
            }
        }
        else
        {
            RemoveSelectableTiles();
            isMoving = false;

            GetCurrentTile();
        }
    }

    public void StartTurn()
    {
        isMyTurn = true;
        if (this.gameObject.CompareTag(StaticDataManager.PLAYER_TAG))
        {
            GameObject selectGrid = GameObject.FindGameObjectWithTag(StaticDataManager.SELECT_GRID_TAG);
            selectGrid.transform.localScale = Vector3.one;
            selectGrid.transform.position = GameObject.FindGameObjectWithTag(StaticDataManager.PLAYER_TAG).transform.position;
        }
    }

    public void EndTurn()
    {
        isMyTurn = false;
        if (this.gameObject.CompareTag(StaticDataManager.PLAYER_TAG))
        {
            GameObject selectGrid = GameObject.FindGameObjectWithTag(StaticDataManager.SELECT_GRID_TAG);
            selectGrid.transform.localScale = Vector3.zero;
        }
    }

    public void FindPath(TileMonoBehaviour targetTile)
    {
        ComputeAdjacencyList(targetTile);
        GetCurrentTile();

        List<TileMonoBehaviour> openList = new List<TileMonoBehaviour>();
        List<TileMonoBehaviour> closeList = new List<TileMonoBehaviour>();
         
        currentTile.h = Vector2.Distance(currentTile.transform.position, targetTile.transform.position);
        currentTile.f = currentTile.h;

        currentTile.isCalculatePath = true;
        openList.Add(currentTile);

        int process_count = 0;

        while (openList.Count > 0)
        {
            //Debug.Log($"Current openList count {openList.Count}");
            TileMonoBehaviour t = FindLowestF(openList);

            closeList.Add(t);

            if (t == targetTile)
            {
                //Debug.Log("Tile :: 遍历到目标Tile，开始寻找最接近的Tile");

                actualTargetTile = FindEndTile(t);
                //actualTargetTile = t;
                MoveToTile(actualTargetTile);

                return;
            }

            process_count += 1;


            foreach (TileMonoBehaviour tile in t.adjacencyList)
            {
                if (closeList.Contains(tile))
                {
                    // TODO
                }
                else if (openList.Contains(tile))
                {
                    // 看看是否还有更快的路径
                    float tempG = t.g + Vector2.Distance(tile.transform.position, t.transform.position);

                    if (tempG < tile.g)
                    {
                        tile.parentTile = t;

                        tile.g = tempG;
                        tile.f = tile.g + tile.h;
                    }
                }
                else
                {
                    // 计算路径
                    tile.parentTile = t;

                    tile.g = t.g + Vector2.Distance(tile.transform.position, t.transform.position);
                    tile.h = Vector2.Distance(tile.transform.position, targetTile.transform.position);

                    tile.f = tile.g + tile.h;

                    tile.isCalculatePath = true;
                    openList.Add(tile);
                }
            }
        }
    }

    protected TileMonoBehaviour FindEndTile(TileMonoBehaviour t)
    {
        Stack<TileMonoBehaviour> tempPath = new Stack<TileMonoBehaviour>();

        TileMonoBehaviour next = t.parentTile;

        while (next != null)
        {
            tempPath.Push(next);
            next = next.parentTile;
        }

        if (tempPath.Count <= move)
        {
            Debug.Log("Tile :: 提前结束了");

            // TODO 在这里开始计算这个目标Tile跟掩体的向量
            Vector2 vector = t.parentTile.transform.position - t.transform.position;
            
            // TODO 然后拿这个向量跟玩家附近的掩体进行向量对比
            return t;
        }

        TileMonoBehaviour endTile = null;

        for (int i = 0; i <= move; i++)
        {
            endTile = tempPath.Pop();
        }

        Debug.Log("Tile :: 从tempPath里面提取的");
        return endTile;

    }

    private TileMonoBehaviour FindLowestF(List<TileMonoBehaviour> list)
    {
        TileMonoBehaviour lowest = list[0];

        foreach (TileMonoBehaviour t in list)
        {
            if (t.f < lowest.f)
            {
                lowest = t;
            }
        }

        list.Remove(lowest);

        return lowest;
    }

    protected void RemoveSelectableTiles()
    {
        if (currentTile != null)
        {
            currentTile.isCurrent = false;
            currentTile = null;
        }
         
        foreach(TileMonoBehaviour tile in selectableTiles)
        {
            tile.ResetParam();
        }

        selectableTiles.Clear();
    }

    void CalculateHeading(Vector3 targetPosition)
    {
        heading = targetPosition - transform.position;
        heading.Normalize();
    }

    void SetHorizontalVelocity()
    {
        moveVelocity = heading * moveSpeed;
    }

    private void SetState(bool isJumpingUp = false, bool isFallingDown = false, bool isMovingEdge = false)
    {
        this.isJumpingUp = isJumpingUp;
        this.isFallingDown = isFallingDown;
        this.isMovingEdge = isMovingEdge;
    }
}
